package name.cyrax.red.jpv.impl.funcs.core;

import name.cyrax.red.jpv.api.adapters.DoubleAdapter;
import name.cyrax.red.jpv.api.adapters.IntegerAdapter;
import name.cyrax.red.jpv.api.adapters.LongAdapter;
import name.cyrax.red.jpv.api.adapters.NumericAdapter;
import name.cyrax.red.jpv.api.funcs.BoundUnaryFunctor;
import name.cyrax.red.jpv.api.funcs.FunctorInstance;
import name.cyrax.red.jpv.api.funcs.Invocable;
import name.cyrax.red.jpv.api.funcs.info.FunctorMethod;
import name.cyrax.red.jpv.api.funcs.info.ValueType;
import name.cyrax.red.jpv.impl.adapters.IntegerImpl;
import name.cyrax.red.jpv.impl.vectors.VectorFactories;

public abstract class FunctorUnaryIntegerImpl
	extends FunctorAbstractUnaryImpl<IntegerAdapter> implements BoundUnaryFunctor<Integer>
{
	private static final String OP_METHOD_NAME = "opInteger"; 
	private static final ValueType OP_RESULT_TYPE = ValueType.INTEGER; 
	private static final Class<?> OP_METHOD_CLASS = FunctorUnaryIntegerImpl.class; 

	private final FunctorInstance funcInstance = getFunctorInstance(
		FunctorMethod.newMethod(OP_METHOD_CLASS,
			OP_METHOD_NAME, OP_RESULT_TYPE, ValueType.UNKNOWN));

	@Override public IntegerAdapter newAdapter(final IntegerAdapter p0) {
		return newAdapter(funcInstance, p0);
	}
	@Override public IntegerAdapter newAdapter(final LongAdapter p0) {
		return newAdapter(funcInstance, p0);
	}
	@Override public IntegerAdapter newAdapter(final DoubleAdapter p0) {
		return newAdapter(funcInstance, p0);
	}

	public abstract int opInteger(int v0);
	public abstract int opInteger(long v0);
	public abstract int opInteger(double v0);
	
	private abstract class UnaryIntegerImpl<N extends NumericAdapter> extends IntegerImpl {
		private final FunctorInstance fi;
		private final N p0;		

		public UnaryIntegerImpl(FunctorInstance fi, N p0)
		{
			this.fi = fi;
			this.p0 = p0;
		}			
		@Override public FunctorInstance getFunctorInstance() {
			return fi;
		}
		@Override public NumericAdapter getParam(int index) {
			return switchOf(index, p0);
		}
		@Override public int size() { return p0.size(); }
	}; 
	
	
	final IntegerAdapter newAdapter(FunctorInstance fi, final IntegerAdapter p0) {
		return checkAdapterType(new UnaryIntegerImpl<IntegerAdapter>(fi, p0) {
			@Override public int getInteger(int index) {
				return opInteger(p0.getInteger(index));
			}
		});
	}
	final IntegerAdapter newAdapter(FunctorInstance fi, final LongAdapter p0) {
		return checkAdapterType(new UnaryIntegerImpl<LongAdapter>(fi, p0) {
			@Override public int getInteger(int index) {
				return opInteger(p0.getLong(index));
			}
		});
	}
	final IntegerAdapter newAdapter(FunctorInstance fi, final DoubleAdapter p0) {
		return checkAdapterType(new UnaryIntegerImpl<DoubleAdapter>(fi, p0) {
			@Override public int getInteger(int index) {
				return opInteger(p0.getDouble(index));
			}
		});
	}		
	
	protected final Invocable<Integer> newInvocable0(Invocable<?> v0)
	{
		return new UnaryInvocableImpl<>(VectorFactories.getInteger(), v0);
	}
	
	@Override public Invocable<Integer> newInstance(Invocable<?> v0)
	{
		return newInvocable0(v0);
	}
}
